/*
	ACPI_tables.c

	contains main ACPI related functionality

	Author: Aidan Goddard, 17/5/13
*/

#include"headers/ACPI_tables.h"
#include"headers/kernel_tables.h"

//#include"../setup/headers/printf.h"


/*
	memcmp
	param 1 - pointer to first block of memory
	param 2 - pointer to second block of memory
	param 3 - number of bytes to compare

	Simple memory comparison function. Compares first N bytes of
	the 2 blocks of memory given by params 1 and 2. Returns 0 when
	they are equal and returns the index of the offending bytes
	when they arent equal.
*/
static uint32_t memcmp(void *test_1, void *test_2, uint32_t n)
{
	int i;
	for(i = 0;; i++)
	{
		if(i == n)
		{
			return 0;
		}
		char char_1 = *((char*)test_1 + i);
		char char_2 = *((char*)test_2 + i);
		if(char_1 != char_2)
		{
			return n;
		}
	}
}

/*
	GetRSDP

	params - none

	Finds the ACPI RSDP and verifies it. Returns a pointer to the
	RSDP on success or NULL on failure.
*/
static ACPI_RSDP *GetRSDP()
{
	// pointer
	ACPI_RSDP *rsdp = NULL;

	// get pointer to start of EBDA (Extended BIOS Data Area)
	uint16_t *ebda_ptr = (uint16_t*)(0x40e + KOFFSET);
	void *ebda = (void*)(uint64_t)*ebda_ptr + (uint64_t)(KOFFSET >> 4);	// add kernel offset to ebda ptr
	ebda = (void*)((uint64_t)ebda << 4);

	// search it for the RSDP signature
	char *rsdp_id = "RSD PTR ";
	while(ebda < (void*)(0x100000 + KOFFSET))
	{
		// check the signature
		if(memcmp(ebda, rsdp_id, 8) == 0)
		{
			// get pointer to the table
			rsdp = ebda;
			//printf("\n[ ACPI  ] Found RSDP 0x%a", rsdp);
			break;
		}

		// loop over, move to next boundary
		ebda = (void*)((uint64_t)ebda + 16);
	}

	// perform checksum (ACPI 1.0)
	uint8_t checksum = 0;
	uint8_t *ptr = (uint8_t*)rsdp;
	int i;
	for(i = 0; i < 20; i++)
	{
		checksum += ptr[i];
		//printf("\n0x%x -> 0x%x -> %c at 0x%a", checksum, ptr[i], ptr[i], &ptr[i]);
	}

	// check it is zero
	if(checksum != 0)
	{
		//printf("\n[ ACPI  ] CHECKSUM ERROR");
		return NULL;
	}

	// checksum valid
	// return the poiner
	return rsdp;
}

/*
	GetRSDT

	params - none

	Finds the ACPI RSDT and verifies it. Returns a pointer to the
	RSDT on success or NULL on failure.
*/
static ACPI_RSDT *GetRSDT()
{
	// get pointer to verified RSDP
	ACPI_RSDP *rsdp = GetRSDP();

	if(rsdp == NULL)
	{
		//printf("\n[ ACPI  ] ACPI RSDP not present");
		return NULL;
	}

	// get the RSDT address
	ACPI_RSDT *rsdt = (ACPI_RSDT*)(uint64_t)rsdp->RSDT_address;

	// perform checksum
	uint8_t checksum = 0;
	uint8_t *ptr = (uint8_t*)rsdt;
	int i;
	for(i = 0; i < rsdt->length; i++)
	{
		checksum += ptr[i];
		//printf("\n%u", checksum);
	}

	// check it is zero
	if(checksum != 0)
	{
		return NULL;
	}

	return rsdt;
}



/*
	ACPIGetMADTBase

	params - none

	Finds the ACPI MADT and verifies it. Returns a pointer to the
	MADT on success or NULL on failure.
*/
ACPI_MADT *ACPIGetMADTBase()
{
	// get a pointer to the verified RSDT
	ACPI_RSDT *rsdt = GetRSDT();

	if(rsdt == NULL)
	{
		//printf("\n[ ACPI  ] ACPI RSDT not present.");
		return NULL;
	}

	// the pointer to return
	ACPI_MADT *madt = NULL;

	// now go through each pointer until the MADT signature is found
	char *madt_sig = "APIC";
	uint32_t *entries = (uint32_t*)rsdt;
	int i;
	for(i = 36; i < rsdt->length; i += 4)
	{
		// get a pointer to the start of this entry
		char *entry = (char*)(uint64_t)entries[i/4];

		// check the signature
		if(memcmp(entry, madt_sig, 4) == 0)
		{
			madt = (ACPI_MADT*)entry;
			break;
		}
	}

	// check if NULL
	if(madt == NULL)
	{
		//printf("\n[ ACPI  ] ACPI MADT not found.");
		return NULL;
	}

	// perform a checksum
	uint8_t checksum = 0;
	uint8_t *ptr = (uint8_t*)madt;
	for(i = 0; i < madt->length; i++)
	{
		checksum += ptr[i];
		//printf("\n%u", checksum);
	}

	// check it is zero
	if(checksum != 0)
	{
		//printf("\n[ ACPI  ] ACPI MADT checksum error.");
		return NULL;
	}

	return madt;
}




/*
	ACPIGetFADTBase

	params - none

	Finds the ACPI FADT and verifies it. Returns a pointer to the
	FADT on success or NULL on failure.
*/
ACPI_FADT *ACPIGetFADTBase()
{
	// get a pointer to the verified RSDT
	ACPI_RSDT *rsdt = GetRSDT();

	if(rsdt == NULL)
	{
		//printf("\n[ ACPI  ] ACPI RSDT not present.");
		return NULL;
	}

	// the pointer to return
	ACPI_FADT *fadt = NULL;

	// now go through each pointer until the FADT signature is found
	char *fadt_sig = "FACP";
	uint32_t *entries = (uint32_t*)rsdt;
	int i;
	for(i = 36; i < rsdt->length; i += 4)
	{
		// get a pointer to the start of this entry
		char *entry = (char*)(uint64_t)entries[i/4];

		// check the signature
		if(memcmp(entry, fadt_sig, 4) == 0)
		{
			fadt = (ACPI_FADT*)entry;
			break;
		}
	}

	// check if NULL
	if(fadt == NULL)
	{
		//printf("\n[ ACPI  ] ACPI FADT not found.");
		return NULL;
	}

	// perform a checksum
	uint8_t checksum = 0;
	uint8_t *ptr = (uint8_t*)fadt;
	for(i = 0; i < fadt->length; i++)
	{
		checksum += ptr[i];
		//printf("\n%u", checksum);
	}

	// check it is zero
	if(checksum != 0)
	{
		//printf("\n[ ACPI  ] ACPI FADT checksum error.");
		return NULL;
	}

	return fadt;
}



/*
	ACPIGetDSDTBase

	params - none

	Finds the ACPI DSDT and verifies it. Returns a pointer to the
	DSDT on success or NULL on failure.
*/
ACPI_DSDT *ACPIGetDSDTBase()
{
	// get the FADT
	ACPI_FADT *fadt = ACPIGetFADTBase();

	// now get the DSDT base from the  FADT
	uint32_t dsdt = fadt->DSDT;
	ACPI_DSDT *DSDT = (ACPI_DSDT*)(uint64_t)dsdt;

	// perform a checksum on it
	uint8_t checksum = 0;
	uint8_t *ptr = (uint8_t*)DSDT;
	int i;
	for(i = 0; i < DSDT->length; i++)
	{
		checksum += ptr[i];
		//printf("\n%u", checksum);
	}

	// check it is zero
	if(checksum != 0)
	{
		//printf("\n[ ACPI  ] ACPI DSDT checksum error.");
		return NULL;
	}

	// valid, now return it
	return DSDT;
}



/*
	ACPIGetHPETBase

	params - none

	Finds the ACPI HPET and verifies it. Returns a pointer to the
	HPET on success or NULL on failure.
*/
ACPI_HPET *ACPIGetHPETBase()
{
	// get a pointer to the verified RSDT
	ACPI_RSDT *rsdt = GetRSDT();

	if(rsdt == NULL)
	{
		//printf("\n[ ACPI  ] ACPI RSDT not present.");
		return NULL;
	}

	// the pointer to return
	ACPI_HPET *hpet = NULL;

	// now go through each pointer until the FADT signature is found
	char *hpet_sig = "HPET";
	uint32_t *entries = (uint32_t*)rsdt;
	int i;
	for(i = 36; i < rsdt->length; i += 4)
	{
		// get a pointer to the start of this entry
		char *entry = (char*)(uint64_t)entries[i/4];

		// check the signature
		if(memcmp(entry, hpet_sig, 4) == 0)
		{
			hpet = (ACPI_HPET*)entry;
			break;
		}
	}

	// check if NULL
	if(hpet == NULL)
	{
		//printf("\n[ ACPI  ] ACPI HPET not found.");
		return NULL;
	}

	// perform a checksum
	uint8_t checksum = 0;
	uint8_t *ptr = (uint8_t*)hpet;
	for(i = 0; i < hpet->length; i++)
	{
		checksum += ptr[i];
		//printf("\n%u", checksum);
	}

	// check it is zero
	if(checksum != 0)
	{
		//printf("\n[ ACPI  ] ACPI HPET checksum error.");
		return NULL;
	}

	return hpet;
}

/*
	ACPIGetMCFGBase

	params - none

	Finds the ACPI MCFG and verifies it. Returns a pointer to the
	MCFG on success or NULL on failure.
*/
ACPI_MCFG *ACPIGetMCFGBase()
{
	// get a pointer to the verified RSDT
	ACPI_RSDT *rsdt = GetRSDT();

	if(rsdt == NULL)
	{
		//printf("\n[ ACPI  ] ACPI RSDT not present.");
		return NULL;
	}

	// the pointer to return
	ACPI_MCFG *mcfg = NULL;

	// now go through each pointer until the FADT signature is found
	char *mcfg_sig = "MCFG";
	uint32_t *entries = (uint32_t*)rsdt;
	int i;
	for(i = 36; i < rsdt->length; i += 4)
	{
		// get a pointer to the start of this entry
		char *entry = (char*)(uint64_t)entries[i/4];

		// check the signature
		if(memcmp(entry, mcfg_sig, 4) == 0)
		{
			mcfg = (ACPI_MCFG*)entry;
			break;
		}
	}

	// check if NULL
	if(mcfg == NULL)
	{
		return NULL;
	}

	// perform a checksum
	uint8_t checksum = 0;
	uint8_t *ptr = (uint8_t*)mcfg;
	for(i = 0; i < mcfg->length; i++)
	{
		checksum += ptr[i];
		//printf("\n%u", checksum);
	}

	// check it is zero
	if(checksum != 0)
	{
		return NULL;
	}

	return mcfg;
}























